/**
 * 
 */
package com.module.bp.event.base;

import java.awt.Cursor;
import java.util.ArrayList;
import java.util.Iterator;

import com.dalonedrau.jogl.engine.Animation;
import com.dalonedrau.jogl.engine.GameApplet;
import com.dalonedrau.jogl.engine.GameDrawable;
import com.dalonedrau.jogl.engine.GlobalConsts;
import com.dalonedrau.jogl.engine.Interface;
import com.dalonedrau.jogl.engine.Time;
import com.dalonedrau.jogl.opengl.animation.AnimConsts;
import com.dalonedrau.jogl.opengl.sprite.SpriteBase;
import com.dalonedrau.jogl.util.UtilityMethods;
import com.dalonedrau.pool.PooledStringBuilder;
import com.dalonedrau.pool.StringBuilderPool;
import com.module.bp.bus.BPHexMap;
import com.module.bp.engine.BPController;
import com.module.bp.engine.Turn;
import com.module.bp.player.BPConsts;
import com.module.bp.player.BPParty;
import com.module.bp.view.OverworldView;

/**
 * @author Donald
 */
public final class MapLandAnimationEvent extends BaseEvent {
	private long	animStartTime	= 0L;
	/** the travel destination. */
	private Integer	destination;
	private int		direction;
	/**
	 * flag indicating the party gets lost, and the animation should reflect as
	 * such.
	 */
	private boolean	getsLost		= false;
	private boolean	moveCompleted	= false;
	private boolean	moveStarted		= false;
	/** the travel source. */
	private Integer	source;
	/**
	 * Creates a new instance of {@link MapLandAnimationEvent}.
	 * @param destination
	 * @param source
	 * @param getsLost
	 */
	public MapLandAnimationEvent(final Integer from, final Integer to,
			final boolean getsLost) {
		super();
		destination = to;
		source = from;
		this.getsLost = getsLost;
		calculateDirection();
	}
	private void calculateDirection() {
		if (source - destination == 20) {
			direction = GlobalConsts.DIR_NORTH;
		} else if (source - destination == -20) {
			direction = GlobalConsts.DIR_SOUTH;
		} else {
			if (source % 2 == 0) { // even numbered column
				if (source - destination == 19) {
					// moving NORTH-EAST
					direction = BPConsts.DIR_NORTHEAST;
				} else if (source - destination == -1) {
					// moving SOUTH-EAST
					direction = BPConsts.DIR_SOUTHEAST;
				} else if (source - destination == 1) {
					// moving SOUTH-WEST
					direction = BPConsts.DIR_SOUTHWEST;
				} else if (source - destination == 21) {
					// moving NORTH-WEST
					direction = BPConsts.DIR_NORTHWEST;
				}
			} else { // odd numbered column
				if (source - destination == -1) {
					// moving NORTH-EAST
					direction = BPConsts.DIR_NORTHEAST;
				} else if (source - destination == -21) {
					// moving SOUTH-EAST
					direction = BPConsts.DIR_SOUTHEAST;
				} else if (source - destination == -19) {
					// moving SOUTH-WEST
					direction = BPConsts.DIR_SOUTHWEST;
				} else if (source - destination == 1) {
					// moving NORTH-WEST
					direction = BPConsts.DIR_NORTHWEST;
				}
			}
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void fire() throws Exception {
		System.out.println("firing move from " + source + "-" + destination);
		if (destination == null || source == null) {
			throw new Exception("Travel destination cannot be null!");
		}
		if (super.happens()) {
			if (moveStarted) {
				if (moveCompleted) {
					endMove();
				} else {
					updatePartyPosition();
				}
			} else {
				startMove();
			}
		}
	}
	/**
	 * Ends the move.
	 * @throws Exception if an error occurs
	 */
	private void endMove() throws Exception {
		// UNBLOCK THE CONTROLS
		Interface.getInstance().removeViewFlag(
				GlobalConsts.VW_03_CONTROLS_BLOCKED);
		// END THE EVENT
		super.setFired(true);
		super.setResolved(true);
		// STOP THE ANIMATION
		Animation.getInstance().stopAllAnimations(
				BPParty.getInstance().getWorldMapSprite()
				);
		// FACE THE PARTY DOWN
		Animation.getInstance().playAnimation(
				BPParty.getInstance().getWorldMapSprite(),
				"party_walk_d",
				AnimConsts.ANIM_GOTO_END_FRAME);
		// TELEPORT THE PARTY TO THE DESTINATION
		BPParty.getInstance().setCurrentHex(
				BPHexMap.getInstance().getHexVector(destination));
		// if party has moves left, 
		// switch back to map selection mode,
		// and load in new travel possibilities
		int movesLeft = BPParty.getInstance().getNumMovesPossible();
		movesLeft -= BPParty.getInstance().getNumMovesTaken();
		if (movesLeft > 0) {
			// LOAD IN NEW TRAVEL POSSIBILITIES
			ArrayList<Integer> list = new ArrayList<Integer>();
			Iterator<Integer> iter = 
				BPController.getInstance().getOverworldGraph()
			.getWalkingAdjacencies(destination, movesLeft).iterator();
			// set highlights for possible move points
			while (iter.hasNext()) {
				int n = iter.next();
				if (n < 460) {
					list.add(n);
				}
			}
			// ASSIGN POSSIBLE TRAVEL HEXES
			OverworldView.getInstance().setMoveRadius(
					list.toArray(new Integer[list.size()]));
			
			// LOAD IN NEW TRAVEL POSSIBILITIES
			list.clear();
			iter = 
				BPController.getInstance().getOverworldGraph()
			.getWalkingAdjacencies(destination, 1).iterator();
			// set highlights for possible move points
			while (iter.hasNext()) {
				int n = iter.next();
				if (n < 460) {
					list.add(n);
				}
			}
			// ASSIGN POSSIBLE TRAVEL HEXES
			OverworldView.getInstance().setPossibleTravelMoves(
					list.toArray(new Integer[list.size()]));
			// SWITCH BACK TO MAP TRAVEL SELECTION MODE
			OverworldView.getInstance().setMapMode(
					BPConsts.OVM_MOVE_SELECTION_MODE);
		} else {
			// END THE MOVEMENT PHASE
			OverworldView.getInstance().setMapMode(BPConsts.OVM_FREE_MODE);
			Turn.getInstance().addEventAfterId(super.getId(), 
					new SmallMessageEvent("HAVE NO MOVES", "LEFT"),
					new EndPhaseEvent());
			// skip to next action phase
			Turn.getInstance().addEvent(Turn.PHASE_PRE_EVENING, 
					new SmallMessageEvent("Skip", "Skip pre-evening"));
			Turn.getInstance().addEvent(Turn.PHASE_PRE_EVENING, 
					new EndPhaseEvent());
			
			Turn.getInstance().addEvent(Turn.PHASE_EVENING, 
					new SmallMessageEvent("Skip", "Skip evening"));
			Turn.getInstance().addEvent(Turn.PHASE_EVENING, 
					new EndPhaseEvent());
			
			Turn.getInstance().addEvent(Turn.PHASE_POST_EVENING, 
					new SmallMessageEvent("Skip", "Skip post-evening"));
			Turn.getInstance().addEvent(Turn.PHASE_POST_EVENING, 
					new EndPhaseEvent());
			
			Turn.getInstance().addEvent(Turn.PHASE_MIDNIGHT, 
					new SmallMessageEvent("Skip", "Skip midnight"));
			Turn.getInstance().addEvent(Turn.PHASE_MIDNIGHT, 
					new EndPhaseEvent());
			
			Turn.getInstance().addEvent(Turn.PHASE_PRE_ACTION, 
					new SmallMessageEvent("Skip", "Skip pre-action"));
			Turn.getInstance().addEvent(Turn.PHASE_PRE_ACTION, 
					new EndPhaseEvent());
		}
		
		
	}
	/**
	 * Starts the animation.
	 * @throws Exception if an error occurs
	 */
	private void startMove() throws Exception {
		Interface.getInstance().assignViewFlag(
				GlobalConsts.VW_03_CONTROLS_BLOCKED);
		OverworldView.getInstance().setMapMode(
				BPConsts.OVM_MOVE_ANIMATION_MODE);
		moveStarted = true;
		animStartTime = Time.getInstance().getGameTime();
		String animation = "";
		switch (direction) {
		case GlobalConsts.DIR_NORTH:
			animation = "party_walk_u";
			break;
		case BPConsts.DIR_NORTHEAST:
		case BPConsts.DIR_SOUTHEAST:
			animation = "party_walk_r";
			break;
		case BPConsts.DIR_NORTHWEST:
		case BPConsts.DIR_SOUTHWEST:
			animation = "party_walk_l";
			break;
		default:
			animation = "party_walk_d";
			break;
		}
		// start the animation
		Animation.getInstance().playAnimation(
				BPParty.getInstance().getWorldMapSprite(),
				animation,
				AnimConsts.ANIM_REPEATS_FROM_STARTKEYFRAME);		
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setUserInput(int input) {
		// TODO Auto-generated method stub
		
	}
	public static final long WALK_DURATION = 3500L;
	public static final float DIV_32 = 0.03125f;
	public static final float DIV_48 = 0.0208333333333333f;
	public static final float DIV_64 = 0.015625f;
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setUserInput(final boolean response) {}
	/** Updates the party sprite's position onscreen. */
	private void updatePartyPosition() {
		GameDrawable.setCursor(Cursor.WAIT_CURSOR);
		System.out.println("updatePartyPosition(" + direction);
		final long NS_Interval =  (long) (WALK_DURATION * DIV_64);
		final long EW_Interval1 = (long) (WALK_DURATION * DIV_32);
		final long EW_Interval2 = (long) (WALK_DURATION * DIV_48);
		long now = Time.getInstance().getGameTime();
		long diff = now - animStartTime;
		SpriteBase sb =
				BPParty.getInstance().getWorldMapSprite().getSprite();
		if (UtilityMethods.getInstance().convertNanoToMilli(diff) > WALK_DURATION) {
			// 4 seconds have passed. end the event
			sb.setPosition(
					sb.getPosition().getX() + sb.getModXl(), 
					sb.getPosition().getY() + sb.getModYb(), 
					sb.getPosition().getZ()
			);
			sb.removeFlag(SpriteBase.FLAG_SPRITE_SHIFT_POSITION);
			sb.setModXl(0);
			sb.setModYb(0);
			moveCompleted = true;
		} else {
			sb.assignFlag(SpriteBase.FLAG_SPRITE_SHIFT_POSITION);
			// update the party's position
			switch (direction) {
			case GlobalConsts.DIR_NORTH:
				// move 64 px in 4 seconds
				// move up 1 pixel every 62500000ns
				int distance = (int) (diff / NS_Interval);
				sb.setModYb(distance);
				break;
			case GlobalConsts.DIR_SOUTH:
				// move 64 px in 4 seconds
				// move down 1 pixel every 62500000ns
				distance = (int) (diff / NS_Interval);
				sb.setModYb(-distance);
				break;
			case BPConsts.DIR_NORTHEAST:
				// move 32 px up in 4 seconds
				// move up 1 pixel every 125000000ns
				int distance1 = (int) (diff / EW_Interval1);
				sb.setModYb(distance1);
				// move 48 px right in 4 seconds
				// move right 1 pixel every 83333333ns
				int distance2 = (int) (diff / EW_Interval2);
				sb.setModXl(distance2);
				break;
			case BPConsts.DIR_NORTHWEST:
				// move 32 px up in 4 seconds
				// move up 1 pixel every 125000000ns
				distance1 = (int) (diff / EW_Interval1);
				sb.setModYb(distance1);
				// move 48 px left in 4 seconds
				// move right 1 left every 83333333ns
				distance2 = (int) (diff / EW_Interval2);
				sb.setModXl(-distance2);
				break;
			case BPConsts.DIR_SOUTHEAST:
				// move 32 px down in 4 seconds
				// move down 1 pixel every 125000000ns
				distance1 = (int) (diff / EW_Interval1);
				sb.setModYb(-distance1);
				// move 48 px right in 4 seconds
				// move right 1 pixel every 83333333ns
				distance2 = (int) (diff / EW_Interval2);
				sb.setModXl(distance2);
				break;
			case BPConsts.DIR_SOUTHWEST:
				// move 32 px down in 4 seconds
				// move down 1 pixel every 125000000ns
				distance1 = (int) (diff / EW_Interval1);
				sb.setModYb(-distance1);
				// move 48 px left in 4 seconds
				// move left 1 pixel every 83333333ns
				distance2 = (int) (diff / EW_Interval2);
				sb.setModXl(-distance2);
				break;
			default:
				PooledStringBuilder s = 
					StringBuilderPool.getInstance().getStringBuilder();
				s.append("MapLandAnimationEvent - invalid travel direction: ");
				s.append(direction);
				Exception ex = new Exception(sb.toString());
				s.returnToPool();
				s = null;
				JOGLErrorHandler.getInstance().fatalError(ex);
				break;
			}
		}
		sb.clearFloatStore();
		sb = null;
	}
}
